package main

import (
	"flag"
	"fmt"
	"github.com/hashicorp/consul/api"
	"github.com/satori/go.uuid"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"net"
	"os"
	"os/signal"
	"service/usr_srv/global"
	"service/usr_srv/handler"
	"service/usr_srv/initialize"
	"service/usr_srv/proto"
	"service/usr_srv/utils"
	"syscall"
)

// 微服务启动入口
func main() {
	// 默认ip如果改成127.0.0.1会导致consul健康检查失败
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 0, "端口号")
	flag.Parse()

	// 如果在启动服务时没有指定port，则动态生成一个可用port
	if *Port == 0 {
		*Port, _ = utils.GetAvailablePort()
	}

	// 初始化
	initialize.InitLogger()
	initialize.InnitConfig()
	initialize.InitDB()

	zap.S().Info("ip:", *IP)
	zap.S().Info("port:", *Port)

	g := grpc.NewServer()
	proto.RegisterUserServer(g, &handler.UserServer{})
	lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))
	if err != nil {
		zap.S().Panic("failed to listen:" + err.Error())
	}

	// 注册健康检查
	grpc_health_v1.RegisterHealthServer(g, health.NewServer())
	// 将当前grpc服务注册到consul
	serviceId := uuid.NewV4().String()
	client, err := RegisterGRPCService(global.ServerConfig.RegisterHost,
		global.ServerConfig.Name, serviceId, *Port, global.ServerConfig.Tags)
	if err != nil {
		zap.S().Error("grpc服务注册失败：", err)
	}

	// 将启动服务的部分放到协程里面，使得后面监听终止信号的部分可以被执行
	go func() {
		err = g.Serve(lis)
		if err != nil {
			zap.S().Panic("failed to start grpc:" + err.Error())
		}
	}()

	// 接收终止信号
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	if err = client.Agent().ServiceDeregister(serviceId); err != nil {
		zap.S().Error("服务注销失败")
	}
	zap.S().Info("服务注销成功")
}

// 函数功能：将grpc服务注册到consul
// 参数说明
// address：待注册的服务的ip
// name：服务名称
// id：服务id
// port：服务端口
// tags：服务标签
func RegisterGRPCService(address, name, id string, port int, tags []string) (*api.Client, error) {
	cfg := api.DefaultConfig()
	// 设置consul服务运行所在的ip和端口
	cfg.Address = fmt.Sprintf("%s:%d", global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)
	//cfg.Address的ip可以是127.0.0.1

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	// 生成健康检查对象
	check := &api.AgentServiceCheck{
		// 这里的ip不可以是127.0.0.1
		GRPC:                           fmt.Sprintf("%s:%d", address, port), // user-web服务的运行地址
		Timeout:                        "5s",                                // 超过此时间说明服务状态不健康
		Interval:                       "5s",                                // 每5s检查一次
		DeregisterCriticalServiceAfter: "30s",                               // 失败多久后注销服务
	}

	// 生成注册对象
	registration := &api.AgentServiceRegistration{
		Name:    name,
		ID:      id,
		Address: address,
		Port:    port,
		Tags:    tags,
		Check:   check,
	}

	// 注册服务
	return client, client.Agent().ServiceRegister(registration)
}
